//import EventEmitter from "events";

import { ClientConnection, gameServer } from "./server";
import { ConnectionCollection } from "./ConnectionCollection";
import { buildConnectionInputFrame, buildGameSyncFrame, GameSyncFrame } from "./shared/gameClient/GameSyncFrame";
import { MsgSyncFrame } from "./shared/gameClient/protocols/MsgSyncFrame";
import { MsgInpFrame } from "./shared/gameClient/protocols/MsgInpFrame";
import { MsgAfterFrames } from "./shared/gameClient/protocols/MsgAfterFrames";
import { GameMsgCall } from "./api/base";
import { MsgSyncState } from "./shared/gameClient/protocols/MsgSyncState";
import { ConnectionStatus } from "tsrpc";


export class Game extends ConnectionCollection {

    //private eventEmitter: EventEmitter = new EventEmitter();

    /**是否启用帧同步功能*/
    public useFrameSync = true;
    /**游戏帧同步中*/
    private _syncing: boolean = false;
    /**当前是否在同步帧中(广播给本游戏中的所有客户端)*/
    get syncing() {
        return this._syncing;
    }
    /**同步帧率(每秒多少帧),默认每秒60帧*/
    public readonly syncFrameRate: number;
    /**帧同步的定时器句柄*/
    private frameUpdateHD!: NodeJS.Timeout;
    /**下次同步的帧索引*/
    private nextSyncFrameIndex = 0;
    /**当前最大帧索引,自动同步帧和输入帧放下一帧,都会推动和同步最大帧索引, 即逻辑上: this.afterFrames 中最后一个索引对应的帧索引(实际性能考虑,同步帧并不会增加afterFrames的长度)*/
    private maxSyncFrameIndex = 0;
    private getAfterFramesIndex(frameIndex: number): number {
        return frameIndex - this.lastStateFrameIndex - 1;
    }
    /**最后记录的游戏状态之后的同步帧,undefined表示空帧,数组索引0对应的帧索引为 this.lastStateDataFrameIndex+1, 可直接使用 this.getAfterFramesIndex(帧索引) 获取帧索引对应的数组索引 */
    private afterFrames: GameSyncFrame[] = [];
    /**空帧对象*/
    public static noneFame: GameSyncFrame = { connectionInputs: [] };


    /**当前最后一次游戏状态数据*/
    public lastStateData: any = {};
    /**当前最后一次游戏状态数据来自哪一帧(即从下一帧开始追帧)*/
    public lastStateFrameIndex = -1;


    /** [开启useFrameSync] 是否启用随机要求客户端同步状态给服务端的功能,方便大大缩短追帧时间, 随机选一个连着的客户端要求发送*/
    public useRandomRequireConnSync = true;
    private _inRandomRequireConnSyncState: boolean = false;
    /**当前是否在随机要求连接同步游戏状态数据到服务端*/
    get inRandomRequireConnSyncState() {
        return this._inRandomRequireConnSyncState;
    }
    /**随机要求连接同步游戏状态数据到服务端的 定时器句柄*/
    private randomRequireConnSyncStateHD!: NodeJS.Timeout;
    /**随机要求连接同步游戏状态数据到服务端的 定时间隔*/
    public randomRequireConnSyncStateInvMs = 20000;
    private randomRequireConnSyncStateListenHD: Function | undefined;
    /**当前要求同步状态的连接ID,即不是所有客户端发来的同步状态都使用的*/
    private requireSyncStateConnId: string | undefined;

    /**
     * 构造函数
     * @param [syncFrameRate] 同步帧率(每秒多少帧),默认每秒60帧
     */
    constructor(syncFrameRate = 60) {
        super();
        this.syncFrameRate = syncFrameRate;
    }

    /**销毁游戏数据,方便快速回收*/
    public dispose(): void {
        this.stopGame();
    }

    /**
     * 停止同步游戏帧
     */
    public stopSyncFrame(): void {
        this._syncing = false;
        clearInterval(this.frameUpdateHD);
    }
    /**
     * 开始同步游戏帧
     */
    public startSyncFrame(): void {
        this.stopSyncFrame();
        this.frameUpdateHD = setInterval(this.onSyncOneFrame.bind(this), 1000 / this.syncFrameRate);
        this._syncing = false;
    }
    /**
     * 处理同步一个游戏帧
     */
    onSyncOneFrame(): void {
        //本帧索引
        var currFrameIndex = this.nextSyncFrameIndex;
        //下一帧计数
        if (this.nextSyncFrameIndex > this.maxSyncFrameIndex) this.maxSyncFrameIndex = this.nextSyncFrameIndex;
        this.nextSyncFrameIndex++;

        //如果当前没有连接,就不需要广播
        if (this.connections.length <= 0) return;

        //本帧数据
        var arrIndex = this.getAfterFramesIndex(currFrameIndex);
        var frame = this.afterFrames[arrIndex];
        if (!frame) {
            //没数据就使用空帧
            frame = Game.noneFame;
        }
        var msg: MsgSyncFrame = {
            frameIndex: currFrameIndex,
            syncFrame: frame,
        }
        //广播给游戏中所有连接
        gameServer.broadcastMsg("SyncFrame", msg, this.connections);
    }


    /**
     * 停止随机要求连接同步游戏状态数据给服务端
     */
    public stopRandomRequireConnSyncState(): void {
        this._inRandomRequireConnSyncState = false;
        clearInterval(this.randomRequireConnSyncStateHD);
        if (this.randomRequireConnSyncStateListenHD) {
            gameServer.unlistenMsg('SyncState', this.randomRequireConnSyncStateListenHD);
        }
    }
    /**
     * 开始随机要求连接同步游戏状态数据给服务端
     */
    public startRandomRequireConnSyncState(): void {
        this.stopRandomRequireConnSyncState();
        this.randomRequireConnSyncStateListenHD = gameServer.listenMsg("SyncState", (call: GameMsgCall<MsgSyncState>) => {
            //必须是服务端当前指定的(信任的),否则不使用这同步数据
            if (this.requireSyncStateConnId != call.conn.connectionId) return;
            //同步服务端状态数据
            this.syncStateData(call.msg.stateData, call.msg.stateFrameIndex);
        });
        this.randomRequireConnSyncStateHD = setInterval(this.onRandomRequireConnSyncState.bind(this), this.randomRequireConnSyncStateInvMs);
        this._inRandomRequireConnSyncState = false;
    }
    /**
     * 处理随机要求连接同步游戏状态数据给服务端
     */
    onRandomRequireConnSyncState(): void {
        //如果当前没有连接,直接返回
        if (this.connections.length <= 0) return;

        var conn: ClientConnection | undefined;
        if (this.requireSyncStateConnId) {
            //已经指定过连接了,直接获取
            conn = this.connectionMap.get(this.requireSyncStateConnId);
        }
        if (!conn || conn.status != ConnectionStatus.Opened) {
            //没指定过或者之前指定的不能用了,则重新随机一个
            var connIndex = Math.floor(Math.random() * this.connections.length);
            conn = this.connections[connIndex];
            this.requireSyncStateConnId = conn.connectionId;
        }

        conn.sendMsg("RequireSyncState", {});
    }
    /**
     * 同步游戏状态数据
     * @param stateData 
     * @param stateFrameIndex 
     */
    public syncStateData(stateData: any, stateFrameIndex: number): void {
        this.lastStateData = stateData;
        var arrIndexEnd = this.getAfterFramesIndex(stateFrameIndex);
        this.lastStateFrameIndex = stateFrameIndex;
        this.afterFrames.splice(0, arrIndexEnd);
    }

    /**
     * 添加连接的输入帧到下一帧
     * @param connectionId 
     * @param inpFrame 
     */
    public addConnectionInpFrame(connectionId: string, inpFrame: MsgInpFrame): void {
        //收到的输入下一帧生效
        var frameIndex = this.nextSyncFrameIndex;
        //更新最大帧索引
        if (frameIndex > this.maxSyncFrameIndex) this.maxSyncFrameIndex = frameIndex;

        var arrIndex = this.getAfterFramesIndex(frameIndex);
        var frame = this.afterFrames[arrIndex];
        if (!frame) this.afterFrames[arrIndex] = frame = buildGameSyncFrame();
        //endSyncFrameIndex
        frame.connectionInputs.push(buildConnectionInputFrame(connectionId, inpFrame));
    }

    /**
     * 给连接发追帧数据(最后状态数据+追帧包)
     * @param connectionId 
     */
    public connectionAfterFrames(conn: ClientConnection): void {
        var aFrames = [];
        for (var fs = this.lastStateFrameIndex + 1, i = 0; fs <= this.maxSyncFrameIndex; fs++, i++) {
            aFrames[i] = this.afterFrames[i] ?? Game.noneFame;
        }
        var msg: MsgAfterFrames = {
            stateData: this.lastStateData,
            stateFrameIndex: this.lastStateFrameIndex,
            afterFrames: aFrames,
            maxSyncFrameIndex: this.maxSyncFrameIndex,
            serverSyncFrameRate: this.syncFrameRate,
        };
        conn.sendMsg("AfterFrames", msg);
    }

    /**开始游戏,根据启用的功能,直接开始游戏服务支持*/
    public startGame(): void {
        this.stopGame();
        if (this.useFrameSync) {
            this.startSyncFrame();
            if (this.useRandomRequireConnSync) {
                this.startRandomRequireConnSyncState();
            }
        }
    }
    /**停止游戏内相关功能,并回收或重置相关数据*/
    public stopGame(): void {
        this.stopSyncFrame();
        this.stopRandomRequireConnSyncState();
        this.lastStateData = {};
        this.lastStateFrameIndex = -1;
        this.afterFrames.length = 0;
        this.nextSyncFrameIndex = 0;
        this.maxSyncFrameIndex = -1;
        this.clearAllConnections();
    }

}